class: title-slide # ER018 - Analyzing Business Relations & Documents ## PVA1 ### Arbeiten mit Zeichenketten (strings) <br> <br> <br> <br> <br> <br> <br> ### FS 2024 <br> ### Prof. Dr. Jörg Schoder .mycontacts[
@FFHS-EconomicResearch
@jfschoder ] --- layout: true <div class="my-footer"></div> <div style="position: absolute;left:400px;bottom:10px;font-size:9px">
Prof. Dr. Jörg Schoder</div> --- name: agenda class: left .blockquote[Agenda] ## Arbeiten mit Zeichenketten (strings) * (Sehr kurze) Einführung in R-Objekte * Zeichenketten in Base-R und das stringr-Paket * Regelhafte Ausdrücke (regular expressions) * Fortgeschrittene reguläre Ausdrücke --- class: left .blockquote[Intro R-Objekte] ## R als objektorientierte Sprache * Objekte lassen sich in Klassen einteilen * tibbles, data.frames * Vektoren * Funktionen -- * Objekte sind vektorbasiert -- * Variablentypen -- * logisch (`logical`) -- * numerisch * `integer` * `double` -- * kategorial * `character` (Zeichenketten, engl. strings) * `factor`
vgl. ausführlicher <a name=cite-wickham_r_2023></a>[Wickham, Çetinkaya-Rundel, and Grolemund (2023)](#bib-wickham_r_2023), Kapitel 12ff. ??? * logical: binäre Logik (true vs. false) * numerisch: integer (ganze Zahlen), double (Doppelgleitkommazahlen) * kategoriale Daten * strings (Zeichenketten) * factors für überschaubar viele Ausprägungen, die nich alphanumerisch geordnet werden sollen --- class: inverse, center, middle ## Zeichenketten in Base-R .blockquote[strings erzeugen] .blockquote[strings verknüpfen] .blockquote[strings zerlegen] --- class: left .blockquote[strings erzeugen] ## Eingabe mit Anführungzeichen * Einfache Anführungszeichen ```r "hoi!" ``` ``` ## [1] "hoi!" ``` * Doppelte Anführungszeichen ```r 'hoi!' ``` ``` ## [1] "hoi!" ``` * Problem: verschachtelte Anführungszeichen ```r # Ich habe "hoi!" gesagt "Ich habe "hoi!" gesagt." ``` ``` ## Error: <text>:2:12: unerwartetes Symbol ## 1: # Ich habe "hoi!" gesagt ## 2: "Ich habe "hoi ## ^ ``` --- class: left .blockquote[strings erzeugen] ## Escape-Sequenz * Verschachtelte Anführungszeichen: * Variante 1 (`'`) ```r 'Ich habe "hoi!" gesagt.' ``` ``` ## [1] "Ich habe \"hoi!\" gesagt." ``` * Variante 2 `\"` (Escape Sequenz!) ```r "Ich habe \"hoi!\" gesagt." ``` ``` ## [1] "Ich habe \"hoi!\" gesagt." ``` --- class: left .blockquote[strings erzeugen] ## Weitere Anwendungsfälle für Escape Sequenzen * Neue Zeile: `\n` ```r "Mit dem Pfeil, dem Bogen Durch Gebirg und Tal Kommt der Schütz gezogen Früh am Morgenstrahl." ``` ``` ## [1] "Mit dem Pfeil, dem Bogen\nDurch Gebirg und Tal\nKommt der Schütz gezogen\nFrüh am Morgenstrahl." ``` * Unicode-Symbole ([Unicode-Chart](https://www.unicode.org/charts/)): * 8 Hexcode-Symbole: `\U` * 8 Hexcode-Symbole: `\u` ??? Escape sequences You might have been surprised at the output from the last part of the last exercise. How did you get two lines from one string, and how did you get that little globe? The key is the \. A sequence in a string that starts with a \ is called an escape sequence and allows us to include special characters in our strings. You saw one escape sequence in the first exercise: \" is used to denote a double quote. In "hello\n\U1F30D" there are two escape sequences: \n gives a newline, and \U followed by up to 8 hex digits sequence denotes a particular Unicode character. Unicode is a standard for representing characters that might not be on your keyboard. Each available character has a Unicode code point: a number that uniquely identifies it. These code points are generally written in hex notation, that is, using base 16 and the digits 0-9 and A-F. You can find the code point for a particular character by looking up a code chart. If you only need four digits for the codepoint, an alternative escape sequence is \u. When R comes across a `\` it assumes you are starting an escape, so if you actually need a backslash in your string you'll need the sequence `\\`. --- class: left ## Zahlenformate .pull-left[ * `format()`: fix vs. wissenschaftlich ```r x <- pi x ``` ``` ## [1] 3.141593 ``` ] -- .pull-right[ <br> ```r format(x, scientific = TRUE) ``` ``` ## [1] "3.141593e+00" ``` ] -- .pull-left[ * `as.character()` ```r as.character(x) ``` ``` ## [1] "3.14159265358979" ``` ] ??? wissenschaftlich: `\(3,1415*10^0\)` --- class: left .blockquote[strings verknüpfen] ## `paste()`-Funktion ```r hopp <- paste("H","O","P","P") hopp ``` ``` ## [1] "H O P P" ``` ```r paste("H","O","P","P", sep = "-") ``` ``` ## [1] "H-O-P-P" ``` ```r paste(c("Hey","Ach","Mach"), "Du") ``` ``` ## [1] "Hey Du" "Ach Du" "Mach Du" ``` --- class: left .blockquote[strings verknüpfen] ## Beispiel: Old MacDonald... .panelset[ .panel[.panel-name[Funktion definieren] ```r old_mac <- function(animal, animal_goes){ eieio <- paste("E", "I", "E", "I", "O", sep = "-") old_mac <- "Old MacDonald had a farm" writeLines(c( old_mac, eieio, paste("And on his farm he had a", animal), eieio, paste(c("Here", "There", "Everywhere"), "a", c(animal_goes, animal_goes, paste(rep(animal_goes, 2), collapse = "-")), collapse = ", "), old_mac, eieio)) } ``` .quellePanURL[Quelle: [C. Wickham, datacamp](https://app.datacamp.com/learn/courses/string-manipulation-with-stringr-in-r).] ] .panel[.panel-name[Beispiel 1] ```r old_mac("cow","moo") ``` ``` ## Old MacDonald had a farm ## E-I-E-I-O ## And on his farm he had a cow ## E-I-E-I-O ## Here a moo, There a moo, Everywhere a moo-moo ## Old MacDonald had a farm ## E-I-E-I-O ``` ] .panel[.panel-name[Beispiel 2] ```r old_mac("dog","wolf") ``` ``` ## Old MacDonald had a farm ## E-I-E-I-O ## And on his farm he had a dog ## E-I-E-I-O ## Here a wolf, There a wolf, Everywhere a wolf-wolf ## Old MacDonald had a farm ## E-I-E-I-O ``` ] ] --- class: left .blockquote[strings zerlegen] ## `strsplit()`-Funktion .panelset[ .panel[.panel-name[Beispiele] ```r strsplit(hopp,split = " ") ``` ``` ## [[1]] ## [1] "H" "O" "P" "P" ```
Output ist eine Liste. ```r test_dates<-c("24-03-2024","25-03-2024","26-03-2024","27-03-2024") date_split <- strsplit(test_dates, split = "-") class(date_split) ``` ``` ## [1] "list" ``` ] .panel[.panel-name[Listen] * Anzahl Listen und Anzahl Elemente im ersten Teilobjekt ```r length(date_split) ``` ``` ## [1] 4 ``` ```r length(date_split[[1]]) ``` ``` ## [1] 3 ``` * Inhalt des ersten Teilobjekts der Liste ```r date_split[1] ``` ``` ## [[1]] ## [1] "24" "03" "2024" ``` ] ] ??? * arbeiten mit Listen ist eher kompliziert, daher empfiehlt es sich... * ...Zerlegungen nicht mit der Base-R-Funktion strsplit() zu machen * sondern auf tidyverse-Pakete zurückzugreifen * allgemein: stringr-Paket * speziell für Datumsangeben: lubridate * bevor wir das stringr-Paket ausführlicher anschauen, ein kurzer Exkurs ins lubridate-Paket --- class: inverse, center, middle ## strings verarbeiten im tidyverse .blockquote[Exkurs: lubridate für Datumsangaben] .blockquote[stringr-Paket] .blockquote[Beispiel: Vornamenhitparade CH] --- class: left .blockquote[Exkurs: lubridate für Datumsangaben] ## Verarbeitung von Datumsangaben im tidyverse * **lubridate**-Bibliothek ist Teil des **tidyverse**-Metapakets * Datumsobjekt erzeugen: ```r library(tidyverse) dates <- dmy(test_dates) # transformieren in Date-Objekt ``` -- * Zerlegung in Tag, Monat, Jahr .pull-left[ ```r day(dates) ``` ``` ## [1] 24 25 26 27 ``` ```r year(dates) ``` ``` ## [1] 2024 2024 2024 2024 ``` ] -- .pull-right[ ```r month(dates) ``` ``` ## [1] 3 3 3 3 ``` ```r month(dates,label = TRUE) ``` ``` ## [1] Mrz Mrz Mrz Mrz ## 12 Levels: Jan < Feb < Mrz < Apr < Mai < Jun < Jul < Aug < Sep < ... < Dez ``` ] ??? * **lubridate**-Paket mit speziellen Funktionen für Datumsangaben --- class: left .blockquote[stringr-Paket] ## Verarbeitung von Zeichenketten (strings) im **tidyverse** .panelset[ .panel[.panel-name[Kurzüberblick] * Aufbauend auf stringi * Wird mit dem **tidyverse**-Metapaket automatisch geladen * Als Teil des **tidyverse** leistungsstark und leicht zu erlernen * Prägnant und konsistent: Alle Funktionen... * beginnen mit `str_` * nutzen als erstes Argument (Input) einen Vektor von Strings * Ergebnisse werden in `tibble`-Objekten gespeichert und können direkt in **Pipes** weiterverarbeitet werden. ] .panel[.panel-name[Vignette] <iframe src="https://stringr.tidyverse.org/" width="100%" height="400px" data-external="1"></iframe> ] ] --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Datenbasis .panelset[ .panel[.panel-name[Datenquelle] <img src="data:image/png;base64,#../../img/PVA1/BFS_20240408.PNG" width="70%" style="display: block; margin: auto;" /> .quellePanURL[
[Download Rohdaten](https://www.bfs.admin.ch/bfs/de/home/statistiken/bevoelkerung/geburten-todesfaelle/vornamen-neugeborene.assetdetail.26565506.html).] ] .panel[.panel-name[Datenimport] * Aufbereitete Daten importieren ```r my_in_file <- "BabynamesCH_(BFS_2024).rds" tbl_babynames <- read_rds(xfun::from_root('data','tidy',my_in_file)) ``` * Anzahl Beobachtungen ```r tbl_babynames %>% count() ``` ``` ## # A tibble: 1 × 1 ## n ## <int> ## 1 2430 ``` ] .panel[.panel-name[Daten] * Einblick in die Tabelle (long-Format) ```r head(tbl_babynames,7) ``` ``` ## # A tibble: 7 × 5 ## Vorname gender year rank count ## <chr> <chr> <chr> <dbl> <dbl> ## 1 Emma girls 2022 1 361 ## 2 Mia girls 2022 2 356 ## 3 Sofia girls 2022 3 318 ## 4 Emilia girls 2022 4 292 ## 5 Lina girls 2022 5 289 ## 6 Elena girls 2022 6 280 ## 7 Mila girls 2022 7 267 ``` ] ] --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Länge der Vornamen mit `str_length()` ```r tbl_babynames %>% group_by(year,gender) %>% summarize(min=min(str_length(Vorname)), max=max(str_length(Vorname)), schnitt=mean(str_length(Vorname))) %>% head() ``` ``` ## # A tibble: 6 × 5 ## # Groups: year [3] ## year gender min max schnitt ## <chr> <chr> <int> <int> <dbl> ## 1 2017 boys 3 10 5.03 ## 2 2017 girls 3 9 5.01 ## 3 2018 boys 3 10 5.03 ## 4 2018 girls 3 9 5.01 ## 5 2019 boys 3 10 5.03 ## 6 2019 girls 3 9 5.01 ``` ??? str_length() gibt die Länge einer Zeichenfolge zurück, das heißt, die Anzahl der Zeichen in der Zeichenfolge. Beispiel: str_length("banana") würde 6 zurückgeben, da "banana" sechs Zeichen lang ist. --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Anzahl Zeichen in Vornamen mit `str_count()` .panelset[ .panel[.panel-name[Einfach] ```r tbl_babynames %>% mutate(n_zeichen = str_count(Vorname)) %>% head(5) ``` ``` ## # A tibble: 5 × 6 ## Vorname gender year rank count n_zeichen ## <chr> <chr> <chr> <dbl> <dbl> <int> ## 1 Emma girls 2022 1 361 4 ## 2 Mia girls 2022 2 356 3 ## 3 Sofia girls 2022 3 318 5 ## 4 Emilia girls 2022 4 292 6 ## 5 Lina girls 2022 5 289 4 ``` ] .panel[.panel-name[Muster] ```r tbl_babynames %>% mutate(n_zeichen = str_count(Vorname, "m")) %>% head(5) ``` ``` ## # A tibble: 5 × 6 ## Vorname gender year rank count n_zeichen ## <chr> <chr> <chr> <dbl> <dbl> <int> ## 1 Emma girls 2022 1 361 2 ## 2 Mia girls 2022 2 356 0 ## 3 Sofia girls 2022 3 318 0 ## 4 Emilia girls 2022 4 292 1 ## 5 Lina girls 2022 5 289 0 ``` ] ]
ist "case-sensitive" (
Mia) ??? * Zahl der Zeichen im Vornamen kann auch mit `str_count()` ermittelt werden * darüber hinaus bietet `str_count()` die Möglichkeit zum zählen der Anzahl der Vorkommen eines bestimmten **Musters** in einer Zeichenfolge. Sie geben das Muster an, nach dem gesucht werden soll, und die Funktion gibt die Anzahl der Vorkommen dieses Musters in jeder Zeichenfolge zurück. Wenn das Muster nicht gefunden wird, gibt str_count() 0 zurück. Beispiel: str_count("banana", "a") würde 3 zurückgeben, da das Muster "a" drei Mal in "banana" vorkommt. --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Teile von Vornamen extrahieren mit `str_sub()` .panelset[ .panel[.panel-name[Erste 3 Buchstaben] ```r tbl_babynames %>% mutate(erste3 = str_sub(Vorname, 1, 3)) %>% head() ``` ``` ## # A tibble: 6 × 6 ## Vorname gender year rank count erste3 ## <chr> <chr> <chr> <dbl> <dbl> <chr> ## 1 Emma girls 2022 1 361 Emm ## 2 Mia girls 2022 2 356 Mia ## 3 Sofia girls 2022 3 318 Sof ## 4 Emilia girls 2022 4 292 Emi ## 5 Lina girls 2022 5 289 Lin ## 6 Elena girls 2022 6 280 Ele ``` ] .panel[.panel-name[Letzte 3 Buchstaben] ```r tbl_babynames %>% mutate(letzte3 = str_sub(Vorname, -3, -1)) %>% head() ``` ``` ## # A tibble: 6 × 6 ## Vorname gender year rank count letzte3 ## <chr> <chr> <chr> <dbl> <dbl> <chr> ## 1 Emma girls 2022 1 361 mma ## 2 Mia girls 2022 2 356 Mia ## 3 Sofia girls 2022 3 318 fia ## 4 Emilia girls 2022 4 292 lia ## 5 Lina girls 2022 5 289 ina ## 6 Elena girls 2022 6 280 ena ``` ] ] ??? str_sub(): str_sub() wird verwendet, um Teilzeichenfolgen aus einer Zeichenfolge zu extrahieren. Sie müssen Start- und Endindizes angeben, um den Teil der Zeichenfolge zu definieren, den Sie extrahieren möchten. Das Ergebnis ist eine Teilzeichenfolge mit der Länge, die durch die angegebenen Start- und Endindizes definiert ist. str_subset(): str_subset() wird verwendet, um eine Teilmenge von Zeichenfolgen zu extrahieren, die ein bestimmtes Muster erfüllen. Sie müssen das Muster angeben, nach dem Sie suchen möchten. Das Ergebnis ist eine Teilmenge der ursprünglichen Zeichenfolgen, die das angegebene Muster erfüllen. --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Teile von Vornamen extrahieren mit `str_subset()` ```r tbl_babynames %>% reframe(muster = str_subset(Vorname, pattern = "anna")) ``` ``` ## # A tibble: 24 × 1 ## muster ## <chr> ## 1 Hanna ## 2 Hannah ## 3 Gianna ## 4 Johanna ## 5 Hanna ## 6 Hannah ## 7 Gianna ## 8 Johanna ## 9 Hanna ## 10 Hannah ## # ℹ 14 more rows ``` --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Teile von Vornamen extrahieren mit `str_extract()` .panelset[ .panel[.panel-name[str_extract] * `str_extract()` prüft die Zeichenfolgen auf das Muster... * ...und gibt für Fälle, in denen das Muster nicht vorkommt "NA" aus ```r tbl_babynames %>% reframe(muster = str_extract(Vorname, pattern = "anna")) %>% head(3) ``` ``` ## # A tibble: 3 × 1 ## muster ## <chr> ## 1 <NA> ## 2 <NA> ## 3 <NA> ``` ] .panel[.panel-name[Auszählen] ```r tbl_babynames %>% reframe(muster = str_extract(Vorname, pattern = "anna")) %>% summarize(fehlende = sum(is.na(muster)), nicht_fehlende = sum(!is.na(muster))) ``` ``` ## # A tibble: 1 × 2 ## fehlende nicht_fehlende ## <int> <int> ## 1 2406 24 ``` ] .panel[.panel-name[Unterschied] * `str_subset()` gibt gesamte Zeichenkette aus, `str_extract()` nur das Muster ```r tbl_babynames %>% reframe(muster = str_extract(Vorname, pattern = "anna")) %>% filter(!is.na(muster)) %>% head(4) ``` ``` ## # A tibble: 4 × 1 ## muster ## <chr> ## 1 anna ## 2 anna ## 3 anna ## 4 anna ``` ] ] ??? * Während `str_subset()` die gesamte Zeichenfolge ausgibt, die das Muster enthält... * ...gibt `str_extract()` nur die gesuchte Zeichenkette aus str_extract() ist ähnlich wie str_subset() in dem Sinne, dass es verwendet wird, um Zeichenfolgen zu extrahieren, die ein bestimmtes Muster erfüllen. Allerdings gibt es einen wesentlichen Unterschied: str_subset(): Gibt die gesamte Teilzeichenfolge zurück, die das Muster erfüllt. str_extract(): Gibt den ersten Teil der Zeichenfolge zurück, der das Muster erfüllt. --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Muster in Vornamen finden mit `str_detect()` .panelset[ .panel[.panel-name[einfach] * Vornamen, die "tz" enthalten ```r tbl_babynames %>% filter(str_detect(Vorname, pattern = "tz")) ``` ``` ## # A tibble: 6 × 5 ## Vorname gender year rank count ## <chr> <chr> <chr> <dbl> <dbl> ## 1 Moritz boys 2022 135 58 ## 2 Moritz boys 2021 130 70 ## 3 Moritz boys 2020 142 62 ## 4 Moritz boys 2019 99 87 ## 5 Moritz boys 2018 141 66 ## 6 Moritz boys 2017 127 74 ``` ] .panel[.panel-name[kombiniert] * Mädchennamen, die "sa" enthalten und mit "L" beginnen ```r tbl_babynames %>% filter(str_detect(Vorname, pattern = "sa")) %>% mutate(muster = str_detect(Vorname, pattern = "L")) %>% head(5) ``` ``` ## # A tibble: 5 × 6 ## Vorname gender year rank count muster ## <chr> <chr> <chr> <dbl> <dbl> <lgl> ## 1 Elisa girls 2022 38 143 FALSE ## 2 Luisa girls 2022 54 118 TRUE ## 3 Lisa girls 2022 82 83 TRUE ## 4 Melissa girls 2022 84 81 FALSE ## 5 Louisa girls 2022 115 61 TRUE ``` ] ] --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Zeichenketten aufteilen `str_split()` .panelset[ .panel[.panel-name[Voller Name] ```r tbl_tmp <- tbl_babynames %>% mutate(Name = paste("Odermatt,", Vorname)) %>% select(Name,gender,year,rank,count) head(tbl_tmp) ``` ``` ## # A tibble: 6 × 5 ## Name gender year rank count ## <chr> <chr> <chr> <dbl> <dbl> ## 1 Odermatt, Emma girls 2022 1 361 ## 2 Odermatt, Mia girls 2022 2 356 ## 3 Odermatt, Sofia girls 2022 3 318 ## 4 Odermatt, Emilia girls 2022 4 292 ## 5 Odermatt, Lina girls 2022 5 289 ## 6 Odermatt, Elena girls 2022 6 280 ``` ] .panel[.panel-name[Aufsplitten] ```r tbl_tmp %>% mutate(Nachname = str_split(Name, pattern = ", ", n = 2) %>% map_chr(1), Vorname = str_split(Name, pattern = ", ", n = 2) %>% map_chr(2)) %>% head() ``` ``` ## # A tibble: 6 × 7 ## Name gender year rank count Nachname Vorname ## <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> ## 1 Odermatt, Emma girls 2022 1 361 Odermatt Emma ## 2 Odermatt, Mia girls 2022 2 356 Odermatt Mia ## 3 Odermatt, Sofia girls 2022 3 318 Odermatt Sofia ## 4 Odermatt, Emilia girls 2022 4 292 Odermatt Emilia ## 5 Odermatt, Lina girls 2022 5 289 Odermatt Lina ## 6 Odermatt, Elena girls 2022 6 280 Odermatt Elena ``` ] ] --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Zeichenketten modifizieren `str_replace()` * (Sehr schmutzige) Alternative zu `str_split()` * "Überschreiben" der Zeichenkette "Odermatt, " mit leerer Zeichenfolge ("") ```r tbl_tmp %>% mutate(Vorname = str_replace(Name, pattern = "Odermatt,", "") %>% str_trim()) %>% head() ``` ``` ## # A tibble: 6 × 6 ## Name gender year rank count Vorname ## <chr> <chr> <chr> <dbl> <dbl> <chr> ## 1 Odermatt, Emma girls 2022 1 361 Emma ## 2 Odermatt, Mia girls 2022 2 356 Mia ## 3 Odermatt, Sofia girls 2022 3 318 Sofia ## 4 Odermatt, Emilia girls 2022 4 292 Emilia ## 5 Odermatt, Lina girls 2022 5 289 Lina ## 6 Odermatt, Elena girls 2022 6 280 Elena ``` ??? `str_trim()` muss verwendet werden, um führende und abschließende Leerzeichen zu entfernen, bevor die Daten ausgegeben werden können. --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Typische Anwendungen für `str_replace()` ```r test_dates ``` ``` ## [1] "24-03-2024" "25-03-2024" "26-03-2024" "27-03-2024" ``` ```r test_dates %>% str_replace(pattern="-",".") ``` ``` ## [1] "24.03-2024" "25.03-2024" "26.03-2024" "27.03-2024" ``` ```r test_dates %>% str_replace_all(pattern="-",".") ``` ``` ## [1] "24.03.2024" "25.03.2024" "26.03.2024" "27.03.2024" ``` --- class: left .blockquote[Beispiel: Vornamenhitparade CH] ## Ausblick: Quantitative Textanalyse .panelset[ .panel[.panel-name[Plot] <img src="data:image/png;base64,#02_Intro_Strings_files/figure-html/unnamed-chunk-45-1.png" width="55%" style="display: block; margin: auto;" /> ] .panel[.panel-name[Code] ```r anfangsbuchstabe <- tbl_babynames %>% mutate(first_letter = substr(Vorname, 1, 1)) %>% group_by(year,gender, first_letter) %>% count() anfangsbuchstabe %>% mutate(gender = factor(gender, levels = c("boys", "girls"))) %>% group_by(gender,year) %>% arrange(desc(n)) %>% slice_max(n=10,order_by = n) %>% ggplot(aes(x=first_letter,y=n,fill=fct_reorder(gender,n))) + geom_col(position = 'dodge') + facet_wrap(~fct_rev(year),scales = "free_x") + scale_fill_discrete(labels = c("girls" = "Mädchen", "boys" = "Knaben")) + labs(x='Anfangsbuchstabe',y='absolute Häufigkeit') + theme_light() + theme(legend.position = "bottom", legend.title = element_blank()) ``` ] ] --- class: inverse, center, middle ## Regelhafte Ausdrücke .blockquote[Regelhafte Ausdrücke mit dem rebus-Paket] .blockquote[Abwechselnde Zeichenfolgen (Alternations)] .blockquote[Zeichenklassen (Character classes)] .blockquote[Wiederholungen] .blockquote[Abkürzungen] --- class: left .blockquote[Regelhafte Ausdrücke mit dem rebus-Paket] ## Regelhafte Ausdrücke ??? * regular expressions (regexp) --- class: left .blockquote[Regelhafte Ausdrücke mit dem rebus-Paket] ## Regelhafte Ausdrücke * Übersicht | Muster | Regelmäßiger Ausdruck | rebus | |----------------------|:---------------------:|:----------:| | Beginn eines strings | `^` | `START` | | Ende eines strings | `$` | `END` | | Einzelne Symbole | `.` | `ANY_CHAR` | | "Belegte" Symbole (`.`,`^`,`$`)| `\.`,`\^`,`\$` | `DOT`,`CARAT`, `DOLLAR`| * Besondere Muster regelhafte Ausdrücke * Alternative Zeichenfolgen (Alternations) * Zeichenklassen (character classes) * Wiederholungen --- class: left .blockquote[Alternative Zeichenfolgen] ## Unterschiedliche Kombinationen von Zeichenfolgen `(winter|sommer)` ```r library(stringr) library(rebus) or("Winter","Sommer") ``` ``` ## <regex> (?:Winter|Sommer) ``` ```r str_view(c("Sommer-Hitze","Traum-Winter","Traumwinter"), pattern = or("[Ww]inter","[Ss]ommer"), match=TRUE,html=TRUE) ```
??? * Obs! Die erste Ausgabe unterscheidet sich leicht von der Eingabe: rebus ergänzt um `?:`, um anzuzeigen, dass es sich um eine "nicht-einfangende Gruppe" von Zeichen (non-capturing group) handelt. Dazu später mehr --- class: left .blockquote[Zeichenklassen (character classes)] ## Identifikation von (definierten) Symbolen .pull-left[ ```r char_class("Aa") ``` ``` ## <regex> [Aa] ``` ```r str_view(c("Frankfurt","Freiheit","Effekt","Schifffahrt"), pattern = char_class("Ff"),html=TRUE ) ```
] -- .pull-right[ ```r negated_char_class("Ff") ``` ``` ## <regex> [^Ff] ``` ```r str_view(c("Frankfurt","Freiheit","Effekt","Schifffahrt"), pattern = negated_char_class("Ff"),html=TRUE ) ```
] ??? * `char_class()` hilft alle Symbole zu finden * Mit `negated_char_class()` können alle Symbole "außer" die angebebenen identifiziert werden. * dies wird in rebus durch das Carat `^`-Symbol innerhalb der eckigen Klammer angezeigt <!-- --- --> <!-- class: left --> <!-- ## Besonderheiten bei Verwendung von Zeichenklassen --> <!-- .pull-left[ --> <!-- * Minuszeichen --> <!-- ```{r} --> <!-- str_view(c("allgemein","Aaron","Sommer-Hitze"), --> <!-- pattern = char_class("Aa-"),html=TRUE --> <!-- ) --> <!-- ``` --> <!-- ] --> <!-- .pull-right[ --> <!-- <br> --> <!-- ```{r} --> <!-- str_view(c("allgemein","Aaron","Sommer-Hitze"), --> <!-- pattern = char_class("-Aa"),html=TRUE --> <!-- ) --> <!-- ``` --> <!-- ] --> <!-- ??? --> <!-- * **Wichtig:** innerhalb von `char_class()` müssen spezielle Symbole wie bspw. `^` oder `$` **nicht** "maskiert" werden. Ein Punkt `.` kann also direkt eingefügt werden. --> <!-- * Ausnahme: Das Minuszeichen. Wenn es in der `char_class()`-Funktion verwendet werden soll, dann sollte es **an erster Stelle** kommen, also bspw. `char_class("-Aa")` --> --- class: left .blockquote[Wiederholungen] ## Zeichenketten mit mehreren gleichartigen Symbolen/Mustern * Übersicht | Muster | Regelmäßiger Ausdruck | rebus | |----------------------|:---------------------:|:----------:| | Optional | `?` | `optional()` | | Null oder mehr | `*` | `zero_or_more()` | | Eins oder mehr | `+` | `one_or_more()` | | Zwischen m- und n-mal | `{m,n}` | `repeated()`| * Beispiel ```r str_view(c("Frankfurt","Freiheit","Effekt","Schifffahrt"), pattern = repeated("Ff",2,3),html=TRUE ) ```
--- class: left .blockquote[Abkürzungen] ## Abgekürzte Eingabe regelhafter Ausdrücke .pull-left[ * $-Symbol und Ziffer(n): ```r DOLLAR %R% char_class("0123456789") ``` ``` ## <regex> \$[0123456789] ``` ] -- .pull-right[ * Eine Ziffer: ```r char_class("0-9") ``` ``` ## <regex> [0-9] ``` ] -- .pull-left[ * Kleinbuchstaben ```r char_class("a-z") ``` ``` ## <regex> [a-z] ``` ] -- .pull-right[ * Großbuchstaben ```r char_class("A-Z") ``` ``` ## <regex> [A-Z] ``` ] --- class: left .blockquote[Abkürzungen] ## Abgekürzte Eingabe regelhafter Ausdrücke .pull-left[ * Ziffern ```r DGT ``` ``` ## <regex> \d ``` ] -- .pull-right[ <br> ```r char_class("0-9") ``` ``` ## <regex> [0-9] ``` ] -- .pull-left[ * Buchstaben ```r WRD ``` ``` ## <regex> \w ``` ] -- .pull-right[ <br> ```r char_class("a-zA-Z0-0_") ``` ``` ## <regex> [a-zA-Z0-0_] ``` ] -- .pull-left[ * Leerzeichen (Whitespace) ```r SPC ``` ``` ## <regex> \s ``` ] --- class: inverse, center, middle ## Fortgeschrittene regelhafte Ausdrücke .blockquote[Erfassung (Capturing)] .blockquote[Rückbezüge (Backreferences)] .blockquote[Mustererkennung mit Unicode] --- class: left .blockquote[Erfassung (Capturing)] ## Gruppierung von Teilmustern * Dient der *Gruppierung von Teilen* von Mustern * Erfasste Teile (von Mustern) können in darauffolgenden Schritten referenziert werden. .pull-left[ ```r ANY_CHAR %R% "a" ``` ``` ## <regex> .a ``` ] -- .pull-right[ ```r str_extract(c("Fakten","Katzen"), pattern=ANY_CHAR %R% "a") ``` ``` ## [1] "Fa" "Ka" ``` ] -- .pull-left[ * Erfasste Teile (*captured groups*) werden in regulären Ausdrücken durch `()` angezeigt/eingegeben: ```r capture(ANY_CHAR) %R% "a" ``` ``` ## <regex> (.)a ``` ] -- .pull-right[ * Capturing führt vordergründig (!) zum selben Ergebnis: ```r str_extract(c("Fakten","Katzen"), capture(ANY_CHAR) %R% "a") ``` ``` ## [1] "Fa" "Ka" ``` ] ??? * Capturing ändert nicht das Muster, das abgeglichen wird, entsprechend führt es auch zum selben Ergebnis * Aber: capturing zeigt an, dass wir (in darauffolgenden Schritten) etwas mit dem erfassten Muster machen wollen --- class: left .blockquote[Erfassung (Capturing)] ## Verwendung erfasster Teilmuster * Verwendung in `str_match()` liefert eine Matrix, wobei die... * ...erste Spalte dem Ergebnis von `str_extract()` entspricht. * ...zweite Spalte nur den erfassten Teil des Musters enthält. * Beispiel ```r str_match(c("Fakten","Katze"), pattern=capture(ANY_CHAR) %R% "a") ``` ``` ## [,1] [,2] ## [1,] "Fa" "F" ## [2,] "Ka" "K" ``` ??? * Verwendung in `str_match()` liefert eine Matrix * erste Spalte entspricht dem Ergebnis von `str_extract()` **mit dem exakten Match** * die zweite Spalte enthält dagegen nur den erfassten Teil des Musters in der captured group --- class: left .blockquote[Erfassung (Capturing)] ## Anwendungsbeispiel mit erfassten Gruppen * Beispiel: Dollar-Beträge unter 100$ ```r muster <- DOLLAR %R% DGT %R% optional(DGT) %R% DOT %R% dgt(2) str_view(c("$7.70","$27.00"),pattern = muster) ``` ``` ## [1] │ <$7.70> ## [2] │ <$27.00> ``` * Nutzung erfasster Teilmuster zur getrennten Extraktion von Dollar- und Cent-Beträgen: ```r cap_muster <- DOLLAR %R% capture(DGT %R% optional(DGT)) %R% DOT %R% capture(dgt(2)) str_match(c("$7.70","$27.00"), pattern = cap_muster) ``` ``` ## [,1] [,2] [,3] ## [1,] "$7.70" "7" "70" ## [2,] "$27.00" "27" "00" ``` ??? * Obs! capture-Funktion wird zweimal verwendet: * einmal um die Vorkommastelle(n) und * einmal um die Nachkommastellen --- class: left .blockquote[Rückbezüge (Backreferences)] ## Verweis auf erfasste Teile von Mustern ```r REF1 ``` ``` ## <regex> \1 ``` ```r REF2 ``` ``` ## <regex> \2 ``` --- class: left .blockquote[Mustererkennung mit Unicode] ## Unicode --- class: inverse,center,middle # Wir brauchen eine Pause. --- background-image: url("data:image/png;base64,#http://bit.ly/cs631-donkey") background-size: 80% --- class: left ## Quellenverzeichnis .ref-slide[ <a name=bib-wickham_r_2023></a>[Wickham, H., M. Çetinkaya-Rundel, and G. Grolemund](#cite-wickham_r_2023) (2023). _R for Data Science: Import, Tidy, Transform, Visualize, and Model Data_. 2nd Edition. Beijing Boston Farnham Sebastopol Tokyo: O'Reilly. ISBN: 978-1-4920-9740-2. ]